#pragma once

#include "SNMPException.h"
#include "PduData.h"
#include "VariableBindingsList.h"
#include "Entity.h"
#include "Community.h"
#include "ReceiveDataEventArgs.h"

using namespace SNMP;

namespace SNMP 
{
	//typedef int (__stdcall *ANSWERCB)(int, int);
	//public delegate int GetTheAnswerDelegate(int, int);
	public delegate unsigned long SnmpApiCallbakcDelegate(System::IntPtr hSession, System::IntPtr hWnd, unsigned int wMsg, unsigned int  wParam, int lParam, System::IntPtr lpClientData);
	public delegate SNMPAPI_STATUS UnmanagedSnmpApiCallbakcDelegate(HSNMP_SESSION hSession, HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam, LPVOID lpClientData);
	public delegate void SNMPEventHandler(System::Object^ sender, EventArgs^ e);
	public delegate void SNMPReceiveEventHandler(System::Object^ sender, ReceiveDataEventArgs^ e);

	public ref class Session
	{
	private:
		static const signed long TRAVERSE_MIB_REQUEST_ID = -999;
		HSNMP_SESSION m_hSession;
		HWND m_hWnd;
		unsigned long m_ulngEntityTimeout;
		unsigned long m_ulngEntityRetry;
		Dictionary<String^, Value^>^ m_dicValueCollections;
		bool m_bRecursioning;

	public:
		event SNMPReceiveEventHandler^ ReceiveData;
		event SNMPReceiveEventHandler^ ReceiveTrap;
		event SNMPEventHandler^ Timeout;

	public:
		property unsigned long EntityTimeout
		{
			unsigned long get(void)
			{
				return this->m_ulngEntityTimeout;
			}
			void set(unsigned long value)
			{
				this->m_ulngEntityTimeout = value;
			}
		};

		property unsigned long EntityRetry
		{
			unsigned long get(void)
			{
				return this->m_ulngEntityRetry;
			}
			void set(unsigned long value)
			{
				this->m_ulngEntityRetry = value;
			}
		};

	internal:
		Session(void);
		void CreateSessionHandle(System::IntPtr handle, SnmpApiCallbakcDelegate^ fCallback);
		void CreateSessionHandle(HWND hWnd, SNMPAPI_CALLBACK fCallback);
		static void CreateSessionHandle(System::IntPtr handle, Session^ session);
		//int TakesCallback(ANSWERCB fp, int n, int m);
		//int delegateTakesCallback(GetTheAnswerDelegate^ fp, int n, int m);

	public:
		~Session(void);
		PduData^ CreatePdu(PduData::PduType pduType, signed long requestID, signed long errorStatus, signed long errorIndex, VariableBindingsList^ varbindlist);
		VariableBindingsList^ CreateVariableBindingsList(void);
		Entity^ CreateEntity(System::String^ IPAddress, unsigned long timeout, unsigned long retry);
		Entity^ CreateEntity(System::Net::IPAddress^ IPAddress, unsigned long timeout, unsigned long retry);
		Community^ CreateCommunity(System::String^ context);
		void Close(void);
		//void SendMsg(void);
		//void SendMsg(HSNMP_SESSION session);
		void SendMessage(Entity^ srcEntity, Entity^ dstEntity, Community^ context, PduData^ pdu);
		void SendMessage(System::String^ dstIpAddress, System::String^ community, PduData^ pdu);
		void SendMessage(array<byte>^ originalData);
		void ReceiveMessage(void);
		void Register(void);
		void Register(Entity^ srcEntity, Entity^ dstEntity, Community^ context);
		Dictionary<String^, Value^>^ TraverseMIB(System::String^ ipAddress, System::String^ community, System::String^ oid);
		

	private:
		void TraverseMIB2(System::String^ ipAddress, System::String^ community, System::String^ oid);

	public:
		SNMPAPI_STATUS SnmpApiCallbakc(HSNMP_SESSION hSession, HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam, LPVOID lpClientData);
	};
}